/**
 * Team CJRR ECE 448/449 Senior Design Project
 * for Miami University, Spring & Fall 2013
 *
 * Multifunction Utility Glove
 * - Non-Contact Voltage Sensor
 * - Flashlight
 * - Laser Pointer
 */

/**
 * Copyrights and licenses to go here.
 */

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <util/delay.h>

#define FLEX0_LEVEL 350
#define FLEX1_LEVEL 300

#define HWAKE_SLEEP_BLINK 100
#define HWAKE_SLEEP_DELAY 5000

#define VOLT_SENSE_HIGH_TARGET 10
#define VOLT_SENSE_RANGE_TARGET 49
#define VOLT_SENSE_SAMPLE_TARGET 20
#define VOLT_SENSE_SPEAKER_FREQ 2200
#define VOLT_SENSE_SPEAKER_TIME 100

#define STATE_VSENSE 1 << 0UL
#define STATE_LIGHT 1 << 1UL
#define STATE_LASER 1 << 2UL


const uint8_t pin_flex0    = A7; // right
const uint8_t pin_flex1    = A0; // left
const uint8_t pin_vsense   = A1;
const uint8_t pin_hsense   = A2;

const uint8_t pin_light    =  5; // o
const uint8_t pin_laser    =  4; // o  
const uint8_t pin_speaker  =  3; // o
const uint8_t pin_hwake    =  2; // i
const uint8_t pin_statusgr =  1; // o
const uint8_t pin_statusrd =  0; // o


void system_setup_pins(void);
void system_wake(void);
void system_sleep(void);
void system_toggle_outputs(void);

void alert_mode(uint16_t fa, float fa_mul, uint16_t fb, float fb_mul,
   uint16_t t, float t_mul);
void alert_onwake(void);
void alert_onsleep(void);

void volt_sense(void);


void system_setup_pins(void)
{
   DDRD |=
      (1 << pin_light) |   
      (1 << pin_laser) |
      (1 << pin_speaker) |
      (1 << pin_statusgr) |
      (1 << pin_statusrd);
   PORTD |=
      (1 << pin_hwake);
}


void system_wake(void)
{
   sleep_disable();
   detachInterrupt(0);
}


void system_sleep(void)
{
   // pre-sleep
   alert_onsleep();
   sleep_enable();
   attachInterrupt(0, system_wake, LOW);
   
   // sleep
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   cli();
#ifdef sleep_bod_disable
   sleep_bod_disable();
#endif
   sei();
   sleep_cpu();

   // post-sleep
   sleep_disable();
   alert_onwake();
}


void alert_mode(uint16_t fa, float fa_mul, uint16_t fb, float fb_mul,
   uint16_t t, float t_mul)
{
   tone(pin_speaker, fa * fa_mul, t * t_mul);

   PORTD |= (1 << pin_statusgr) | (1 < pin_statusrd);
   _delay_ms(t);
   PORTD &= ~((1 << pin_statusgr) | (1 < pin_statusrd));
   _delay_ms(t);

   tone(pin_speaker, fb * fb_mul, t * t_mul);

   PORTD |= (1 << pin_statusgr) | (1 < pin_statusrd);
   _delay_ms(t);
   PORTD &= ~((1 << pin_statusgr) | (1 < pin_statusrd));
   _delay_ms(t);

   noTone(pin_speaker);
}


void alert_onwake(void)
{
   alert_mode(2200, 1.0f, 2200, 1.5f, 250, 2.0f);
}


void alert_onsleep(void)
{
   alert_mode(2200, 1.5f, 2200, 1.0f, 125, 2.0f);
}


void volt_sense(void)
{
   static int16_t sample_max     =    0;
   static int16_t sample_min     = 1023;
   static int16_t sample_count   =    0;
   static int16_t high_count     =    0;

   /* WARNING! Do not remove the following, magical reads. We need them. */
   analogRead(pin_flex0);
   analogRead(pin_flex1);
   int16_t sample = analogRead(pin_vsense);

   if (sample <= sample_min) {
      sample_min = sample;
   }

   if (sample > sample_max) {
      sample_max = sample;
   }

   ++sample_count;

   if (sample_count == VOLT_SENSE_SAMPLE_TARGET) {
      if (sample_max - sample_min > VOLT_SENSE_RANGE_TARGET) {
         ++high_count;
      }
      else {
         PORTD &= ~(1 << pin_statusrd);
         high_count = 0;
      }

      if (high_count > VOLT_SENSE_HIGH_TARGET) {
         PORTD |= (1 << pin_statusrd);
         tone(pin_speaker, VOLT_SENSE_SPEAKER_FREQ, VOLT_SENSE_SPEAKER_TIME);
      }

      sample_count = 0;
      sample_max = 0;
      // res: Original code had 0 here -- this logically doesn't make sense,
      //      but we'll leave it for now.
      sample_min = 0;
   }

   delay(1);
}


void setup(void)
{
   system_setup_pins();
   alert_onwake();
}


void loop(void)
{
   static uint32_t last_hwake_low = 0;
   static uint32_t state_curr = STATE_VSENSE;
   static uint32_t state_prev = STATE_VSENSE;

   if (PIND & (1 << pin_hwake)) {
      if (abs(millis() - last_hwake_low) >= HWAKE_SLEEP_DELAY) {
         system_sleep();
      }
   }
   else {
      uint16_t flex0_val = analogRead(pin_flex0);
      uint16_t flex1_val = analogRead(pin_flex1);

      switch (state_curr) {
         case STATE_VSENSE:
            volt_sense();

            if (flex0_val < FLEX0_LEVEL && flex1_val < FLEX1_LEVEL) {
               state_prev = state_curr;
               state_curr = STATE_LIGHT;
            }
            else if (flex0_val < FLEX0_LEVEL && flex1_val >= FLEX1_LEVEL) {
               state_prev = state_curr;
               state_curr = STATE_LASER;
            }
         break;

         case STATE_LASER:
            PORTD |= (1 << pin_laser);

            if (flex0_val < FLEX0_LEVEL && flex1_val < FLEX1_LEVEL) {
               PORTD &= ~(1 << pin_laser);
               state_prev = state_curr;
               state_curr = STATE_LIGHT;
            }
            else if (flex0_val >= FLEX0_LEVEL && flex1_val >= FLEX1_LEVEL) {
               PORTD &= ~(1 << pin_laser);
               state_prev = state_curr;
               state_curr = STATE_VSENSE;
            }
         break;

         case STATE_LIGHT:
            PORTD |= (1 << pin_light);

            if (flex0_val < FLEX0_LEVEL && flex1_val >= FLEX1_LEVEL) {
               PORTD &= ~(1 << pin_light);
               state_prev = state_curr;
               state_curr = STATE_LASER;
            }
            else if (flex0_val >= FLEX0_LEVEL && flex1_val >= FLEX1_LEVEL) {
               PORTD &= ~(1 << pin_light);
               state_prev = state_curr;
               state_curr = STATE_VSENSE;
            }
         break;

         default:
            state_curr = STATE_VSENSE;
            state_prev = STATE_VSENSE;
         break;
      }

      last_hwake_low = millis();
   }
}

